namespace SimpleWebServices.Web
{
    using System;
    using System.Web;
    using Moq;
    using SimpleWebServices.Data;
    using SimpleWebServices.Models;
    using Xunit;

    public sealed class HttpRequestBaseExtensionMethodsFacts
    {
        private static readonly string _xxx = new string('x', 36);

        [Fact]
        public void typedef()
        {
            Assert.True(typeof(HttpRequestBaseExtensionMethods).IsStatic());
        }

        [Fact]
        public void op_RawQuery_HttpRequestBaseString()
        {
            string expected = "?querystring";

            var mock = new Mock<HttpRequestBase>();
            mock.SetupGet<string>(x => x.RawUrl).Returns("http://example.com/test?querystring").Verifiable();

            string actual = mock.Object.RawQueryString();

            Assert.Equal<string>(expected, actual);

            mock.VerifyAll();
        }

        [Fact]
        public void op_RawQueryString_HttpRequestBaseEmpty()
        {
            string expected = "?";

            var mock = new Mock<HttpRequestBase>();
            mock.SetupGet<string>(x => x.RawUrl).Returns("http://example.com/test?").Verifiable();

            string actual = mock.Object.RawQueryString();

            Assert.Equal<string>(expected, actual);

            mock.VerifyAll();
        }

        [Fact]
        public void op_RawQueryString_HttpRequestBaseMissing()
        {
            string expected = string.Empty;

            var mock = new Mock<HttpRequestBase>();
            mock.SetupGet<string>(x => x.RawUrl).Returns("http://example.com/test").Verifiable();

            string actual = mock.Object.RawQueryString();

            Assert.Equal<string>(expected, actual);

            mock.VerifyAll();
        }

        [Fact]
        public void op_RawQueryString_HttpRequestBaseNull()
        {
            Assert.Throws<ArgumentNullException>(() => (null as HttpRequestBase).RawQueryString());
        }

        [Fact]
        public void op_RawQuery_HttpRequestBaseString_TokenNull()
        {
            string expected = "?querystring";

            var mock = new Mock<HttpRequestBase>();
            mock.SetupGet<string>(x => x.RawUrl).Returns("http://example.com/test?querystring").Verifiable();

            Token? token = null;
            string actual = mock.Object.RawQueryString(token);

            Assert.Equal<string>(expected, actual);

            mock.VerifyAll();
        }

        [Fact]
        public void op_RawQuery_HttpRequestBaseCastException_TokenNull()
        {
            string expected = "?" + _xxx;

            var mock = new Mock<HttpRequestBase>();
            mock.SetupGet<string>(x => x.RawUrl).Returns("http://example.com/test?" + _xxx).Verifiable();

            Token? token = null;
            string actual = mock.Object.RawQueryString(token);

            Assert.Equal<string>(expected, actual);

            mock.VerifyAll();
        }

        [Fact]
        public void op_RawQueryString_HttpRequestBaseEmpty_TokenNull()
        {
            string expected = string.Empty;

            var mock = new Mock<HttpRequestBase>();
            mock.SetupGet<string>(x => x.RawUrl).Returns("http://example.com/test?").Verifiable();

            Token? token = null;
            string actual = mock.Object.RawQueryString(token);

            Assert.Equal<string>(expected, actual);

            mock.VerifyAll();
        }

        [Fact]
        public void op_RawQueryString_HttpRequestBaseMissing_TokenNull()
        {
            string expected = string.Empty;

            var mock = new Mock<HttpRequestBase>();
            mock.SetupGet<string>(x => x.RawUrl).Returns("http://example.com/test").Verifiable();

            Token? token = null;
            string actual = mock.Object.RawQueryString(token);

            Assert.Equal<string>(expected, actual);

            mock.VerifyAll();
        }

        [Fact]
        public void op_RawQueryString_HttpRequestBaseNull_TokenNull()
        {
            Token? token = null;

            Assert.Throws<ArgumentNullException>(() => (null as HttpRequestBase).RawQueryString(token));
        }

        [Fact]
        public void op_RawQuery_HttpRequestBaseString_Token()
        {
            Token? token = Token.New();
            string expected = string.Concat("?", token, "&querystring");

            var mock = new Mock<HttpRequestBase>();
            mock.SetupGet<string>(x => x.RawUrl).Returns("http://example.com/test?querystring").Verifiable();

            string actual = mock.Object.RawQueryString(token);

            Assert.Equal<string>(expected, actual);

            mock.VerifyAll();
        }

        [Fact]
        public void op_RawQuery_HttpRequestBaseStringGuid_Token()
        {
            Token? token = Token.New();
            string expected = string.Concat("?", token, "&querystring");

            var mock = new Mock<HttpRequestBase>();
            mock.SetupGet<string>(x => x.RawUrl).Returns(string.Concat("http://example.com/test?", Token.New(), "&querystring")).Verifiable();

            string actual = mock.Object.RawQueryString(token);

            Assert.Equal<string>(expected, actual);

            mock.VerifyAll();
        }

        [Fact]
        public void op_RawQuery_HttpRequestBaseInvalidGuid_Token()
        {
            Token? token = Token.New();
            string expected = string.Concat("?", token, "&", _xxx, "&querystring");

            var mock = new Mock<HttpRequestBase>();
            mock.SetupGet<string>(x => x.RawUrl).Returns(string.Concat("http://example.com/test?", _xxx, "&querystring")).Verifiable();

            string actual = mock.Object.RawQueryString(token);

            Assert.Equal<string>(expected, actual);

            mock.VerifyAll();
        }

        [Fact]
        public void op_RawQuery_HttpRequestBaseParams_Token()
        {
            Token? token = Token.New();
            string expected = string.Concat("?", token, "&a=b");

            var mock = new Mock<HttpRequestBase>();
            mock.SetupGet<string>(x => x.RawUrl).Returns("http://example.com/test?a=b").Verifiable();

            string actual = mock.Object.RawQueryString(token);

            Assert.Equal<string>(expected, actual);

            mock.VerifyAll();
        }

        [Fact]
        public void op_RawQuery_HttpRequestBaseParamsGuid_Token()
        {
            Token? token = Token.New();
            string expected = string.Concat("?", token, "&a=b");

            var mock = new Mock<HttpRequestBase>();
            mock.SetupGet<string>(x => x.RawUrl).Returns(string.Concat("http://example.com/test?", Token.New(), "&a=b")).Verifiable();

            string actual = mock.Object.RawQueryString(token);

            Assert.Equal<string>(expected, actual);

            mock.VerifyAll();
        }

        [Fact]
        public void op_RawQuery_HttpRequestBaseEmpty_Token()
        {
            Token? token = Token.New();
            string expected = string.Concat("?", token);

            var mock = new Mock<HttpRequestBase>();
            mock.SetupGet<string>(x => x.RawUrl).Returns("http://example.com/test?").Verifiable();

            string actual = mock.Object.RawQueryString(token);

            Assert.Equal<string>(expected, actual);

            mock.VerifyAll();
        }

        [Fact]
        public void op_RawQuery_HttpRequestBaseEmptyGuid_Token()
        {
            Token? token = Token.New();
            string expected = string.Concat("?", token);

            var mock = new Mock<HttpRequestBase>();
            mock.SetupGet<string>(x => x.RawUrl).Returns("http://example.com/test?" + Token.New()).Verifiable();

            string actual = mock.Object.RawQueryString(token);

            Assert.Equal<string>(expected, actual);

            mock.VerifyAll();
        }

        [Fact]
        public void op_RawQuery_HttpRequestBaseMissing_Token()
        {
            Token? token = Token.New();
            string expected = string.Concat("?", token);

            var mock = new Mock<HttpRequestBase>();
            mock.SetupGet<string>(x => x.RawUrl).Returns("http://example.com/test").Verifiable();

            string actual = mock.Object.RawQueryString(token);

            Assert.Equal<string>(expected, actual);

            mock.VerifyAll();
        }

        [Fact]
        public void op_RawQueryString_HttpRequestBaseNull_Token()
        {
            Assert.Throws<ArgumentNullException>(() => (null as HttpRequestBase).RawQueryString(Token.New()));
        }

        [Fact]
        public void op_RawQuery_HttpRequestBaseString_TokenNull_string()
        {
            string expected = string.Concat("?querystring", "&whence=%2flocation");

            var mock = new Mock<HttpRequestBase>();
            mock.SetupGet<string>(x => x.RawUrl).Returns("http://example.com/test?querystring").Verifiable();

            Token? token = null;
            string actual = mock.Object.RawQueryString(token, "/location");

            Assert.Equal<string>(expected, actual);

            mock.VerifyAll();
        }

        [Fact]
        public void op_RawQuery_HttpRequestBaseString_TokenNull_stringEmpty()
        {
            string expected = "?querystring";

            var mock = new Mock<HttpRequestBase>();
            mock.SetupGet<string>(x => x.RawUrl).Returns("http://example.com/test?querystring&whence=%2freplace").Verifiable();

            Token? token = null;
            string actual = mock.Object.RawQueryString(token, string.Empty);

            Assert.Equal<string>(expected, actual);

            mock.VerifyAll();
        }

        [Fact]
        public void op_RawQuery_HttpRequestBaseString_TokenNull_stringNull()
        {
            string expected = "?querystring";

            var mock = new Mock<HttpRequestBase>();
            mock.SetupGet<string>(x => x.RawUrl).Returns("http://example.com/test?querystring&whence=%2freplace").Verifiable();

            Token? token = null;
            string actual = mock.Object.RawQueryString(token, null);

            Assert.Equal<string>(expected, actual);

            mock.VerifyAll();
        }

        [Fact]
        public void op_RawQuery_HttpRequestBaseStringWhence_TokenNull_string()
        {
            string expected = string.Concat("?querystring", "&whence=%2flocation");

            var mock = new Mock<HttpRequestBase>();
            mock.SetupGet<string>(x => x.RawUrl).Returns("http://example.com/test?querystring&whence=%2freplace").Verifiable();

            Token? token = null;
            string actual = mock.Object.RawQueryString(token, "/location");

            Assert.Equal<string>(expected, actual);

            mock.VerifyAll();
        }

        [Fact]
        public void op_RawQuery_HttpRequestBaseCastException_TokenNull_string()
        {
            string expected = string.Concat("?", _xxx, "&whence=%2flocation");

            var mock = new Mock<HttpRequestBase>();
            mock.SetupGet<string>(x => x.RawUrl).Returns("http://example.com/test?" + _xxx).Verifiable();

            Token? token = null;
            string actual = mock.Object.RawQueryString(token, "/location");

            Assert.Equal<string>(expected, actual);

            mock.VerifyAll();
        }

        [Fact]
        public void op_RawQueryString_HttpRequestBaseEmpty_TokenNull_string()
        {
            string expected = "?whence=%2flocation";

            var mock = new Mock<HttpRequestBase>();
            mock.SetupGet<string>(x => x.RawUrl).Returns("http://example.com/test?").Verifiable();

            Token? token = null;
            string actual = mock.Object.RawQueryString(token, "/location");

            Assert.Equal<string>(expected, actual);

            mock.VerifyAll();
        }

        [Fact]
        public void op_RawQueryString_HttpRequestBaseMissing_TokenNull_string()
        {
            string expected = "?whence=%2flocation";

            var mock = new Mock<HttpRequestBase>();
            mock.SetupGet<string>(x => x.RawUrl).Returns("http://example.com/test").Verifiable();

            Token? token = null;
            string actual = mock.Object.RawQueryString(token, "/location");

            Assert.Equal<string>(expected, actual);

            mock.VerifyAll();
        }

        [Fact]
        public void op_RawQueryString_HttpRequestBaseNull_TokenNull_string()
        {
            Token? token = null;

            Assert.Throws<ArgumentNullException>(() => (null as HttpRequestBase).RawQueryString(token, "/location"));
        }

        [Fact]
        public void op_RawQuery_HttpRequestBaseString_Token_string()
        {
            Token? token = Token.New();
            string expected = string.Concat("?", token, "&querystring", "&whence=%2flocation");

            var mock = new Mock<HttpRequestBase>();
            mock.SetupGet<string>(x => x.RawUrl).Returns("http://example.com/test?querystring").Verifiable();

            string actual = mock.Object.RawQueryString(token, "/location");

            Assert.Equal<string>(expected, actual);

            mock.VerifyAll();
        }

        [Fact]
        public void op_RawQuery_HttpRequestBaseStringGuid_Token_string()
        {
            Token? token = Token.New();
            string expected = string.Concat("?", token, "&querystring", "&whence=%2flocation");

            var mock = new Mock<HttpRequestBase>();
            mock.SetupGet<string>(x => x.RawUrl).Returns(string.Concat("http://example.com/test?", Token.New(), "&querystring")).Verifiable();

            string actual = mock.Object.RawQueryString(token, "/location");

            Assert.Equal<string>(expected, actual);

            mock.VerifyAll();
        }

        [Fact]
        public void op_RawQuery_HttpRequestBaseInvalidGuid_Token_string()
        {
            Token? token = Token.New();
            string expected = string.Concat("?", token, "&", _xxx, "&querystring", "&whence=%2flocation");

            var mock = new Mock<HttpRequestBase>();
            mock.SetupGet<string>(x => x.RawUrl).Returns(string.Concat("http://example.com/test?", _xxx, "&querystring")).Verifiable();

            string actual = mock.Object.RawQueryString(token, "/location");

            Assert.Equal<string>(expected, actual);

            mock.VerifyAll();
        }

        [Fact]
        public void op_RawQuery_HttpRequestBaseParams_Token_string()
        {
            Token? token = Token.New();
            string expected = string.Concat("?", token, "&a=b", "&whence=%2flocation");

            var mock = new Mock<HttpRequestBase>();
            mock.SetupGet<string>(x => x.RawUrl).Returns("http://example.com/test?a=b").Verifiable();

            string actual = mock.Object.RawQueryString(token, "/location");

            Assert.Equal<string>(expected, actual);

            mock.VerifyAll();
        }

        [Fact]
        public void op_RawQuery_HttpRequestBaseParamsGuid_Token_string()
        {
            Token? token = Token.New();
            string expected = string.Concat("?", token, "&a=b", "&whence=%2flocation");

            var mock = new Mock<HttpRequestBase>();
            mock.SetupGet<string>(x => x.RawUrl).Returns(string.Concat("http://example.com/test?", Token.New(), "&a=b")).Verifiable();

            string actual = mock.Object.RawQueryString(token, "/location");

            Assert.Equal<string>(expected, actual);

            mock.VerifyAll();
        }

        [Fact]
        public void op_RawQuery_HttpRequestBaseEmpty_Token_string()
        {
            Token? token = Token.New();
            string expected = string.Concat("?", token, "&whence=%2flocation");

            var mock = new Mock<HttpRequestBase>();
            mock.SetupGet<string>(x => x.RawUrl).Returns("http://example.com/test?").Verifiable();

            string actual = mock.Object.RawQueryString(token, "/location");

            Assert.Equal<string>(expected, actual);

            mock.VerifyAll();
        }

        [Fact]
        public void op_RawQuery_HttpRequestBaseEmptyGuid_Token_string()
        {
            Token? token = Token.New();
            string expected = string.Concat("?", token, "&whence=%2flocation");

            var mock = new Mock<HttpRequestBase>();
            mock.SetupGet<string>(x => x.RawUrl).Returns("http://example.com/test?" + Token.New()).Verifiable();

            string actual = mock.Object.RawQueryString(token, "/location");

            Assert.Equal<string>(expected, actual);

            mock.VerifyAll();
        }

        [Fact]
        public void op_RawQuery_HttpRequestBaseMissing_Token_string()
        {
            Token? token = Token.New();
            string expected = string.Concat("?", token, "&whence=%2flocation");

            var mock = new Mock<HttpRequestBase>();
            mock.SetupGet<string>(x => x.RawUrl).Returns("http://example.com/test").Verifiable();

            string actual = mock.Object.RawQueryString(token, "/location");

            Assert.Equal<string>(expected, actual);

            mock.VerifyAll();
        }

        [Fact]
        public void op_RawQueryString_HttpRequestBaseNull_Token_string()
        {
            Assert.Throws<ArgumentNullException>(() => (null as HttpRequestBase).RawQueryString(Token.New(), "/location"));
        }

        [Fact]
        public void op_TokenizedOfT_HttpRequestBaseNull()
        {
            Assert.Throws<ArgumentNullException>(() => (null as HttpRequestBase).Tokenized<FormSubmission>());
        }

        [Fact]
        public void op_TokenizedOfT_HttpRequestBase()
        {
            FakeRepository.Clear();

            try
            {
                Token token = Token.New();

                FakeRepository.Register<FormSubmission>(new
                {
                    token = token,
                    urn = "urn://example.com/submission/" + Token.New(),
                    xml = "<submission><data /></submission>"
                });

                var mock = new Mock<HttpContextBase>();
                mock.SetupGet<string>(x => x.Request.RawUrl).Returns("/debug/submission?" + token).Verifiable();
                HttpContextFactory.Mock = mock.Object;

                Assert.Equal<Token>(token, HttpContextFactory.Current.Request.Tokenized<FormSubmission>().Token.Value);

                mock.VerifyAll();
            }
            finally
            {
                FakeRepository.Reset();
                HttpContextFactory.Reset();
            }
        }

        [Fact]
        public void op_TokenizedOfT_HttpRequestBase_whenNotInRepository()
        {
            FakeRepository.Clear();

            try
            {
                FakeRepository.Register<FormSubmission>();

                var mock = new Mock<HttpContextBase>();
                mock.SetupGet<string>(x => x.Request.RawUrl).Returns("/debug/submission?" + Token.New()).Verifiable();
                HttpContextFactory.Mock = mock.Object;

                Assert.Null(HttpContextFactory.Current.Request.Tokenized<FormSubmission>());

                mock.VerifyAll();
            }
            finally
            {
                FakeRepository.Reset();
                HttpContextFactory.Reset();
            }
        }

        [Fact]
        public void op_TokenizedOfT_HttpRequestBase_whenNoToken()
        {
            try
            {
                var mock = new Mock<HttpContextBase>();
                mock.SetupGet<string>(x => x.Request.RawUrl).Returns("/debug/submission").Verifiable();
                HttpContextFactory.Mock = mock.Object;

                Assert.Null(HttpContextFactory.Current.Request.Tokenized<FormSubmission>());

                mock.VerifyAll();
            }
            finally
            {
                HttpContextFactory.Reset();
            }
        }

        [Fact]
        public void op_Token_HttpRequestBase()
        {
            Token? expected = Token.New();

            var mock = new Mock<HttpRequestBase>();
            mock.SetupGet<string>(x => x.RawUrl).Returns("http://example.com/test?" + expected).Verifiable();

            Token? actual = mock.Object.Token();

            Assert.Equal<Token?>(expected, actual);

            mock.VerifyAll();
        }

        [Fact]
        public void op_Token_HttpRequestBase_whenRawUrlInvalid()
        {
            var mock = new Mock<HttpRequestBase>();
            mock.SetupGet<string>(x => x.RawUrl).Returns("http://example.com/test?" + _xxx).Verifiable();

            Assert.Null(mock.Object.Token());

            mock.VerifyAll();
        }

        [Fact]
        public void op_Token_HttpRequestBase_whenRawUrlWhence()
        {
            Token? expected = null;

            var mock = new Mock<HttpRequestBase>();
            mock.SetupGet<string>(x => x.RawUrl).Returns("http://example.com/test?whence=%2Fsome-place").Verifiable();

            Token? actual = mock.Object.Token();

            Assert.Equal<Token?>(expected, actual);

            mock.VerifyAll();
        }

        [Fact]
        public void op_Token_HttpRequestBase_whenRawUrlEmpty()
        {
            Token? expected = null;

            var mock = new Mock<HttpRequestBase>();
            mock.SetupGet<string>(x => x.RawUrl).Returns("http://example.com/test?").Verifiable();

            Token? actual = mock.Object.Token();

            Assert.Equal<Token?>(expected, actual);

            mock.VerifyAll();
        }

        [Fact]
        public void op_Token_HttpRequestBase_whenRawUrlMissing()
        {
            Token? expected = null;

            var mock = new Mock<HttpRequestBase>();
            mock.SetupGet<string>(x => x.RawUrl).Returns("http://example.com/test").Verifiable();

            Token? actual = mock.Object.Token();

            Assert.Equal<Token?>(expected, actual);

            mock.VerifyAll();
        }

        [Fact]
        public void op_Token_HttpRequestBase_whenRawUrlParams()
        {
            Token? expected = Token.New();

            var mock = new Mock<HttpRequestBase>();
            mock.SetupGet<string>(x => x.RawUrl).Returns("http://example.com/test?" + expected + "&a=b").Verifiable();

            Token? actual = mock.Object.Token();

            Assert.Equal<Token?>(expected, actual);

            mock.VerifyAll();
        }

        [Fact]
        public void op_Token_HttpRequestBase_whenRawUrlParamsPrefixed()
        {
            Token? expected = null;

            var mock = new Mock<HttpRequestBase>();
            mock.SetupGet<string>(x => x.RawUrl).Returns("http://example.com/test?a=b&").Verifiable();

            Token? actual = mock.Object.Token();

            Assert.Equal<Token?>(expected, actual);

            mock.VerifyAll();
        }

        [Fact]
        public void op_Token_HttpRequestBaseNull()
        {
            Assert.Throws<ArgumentNullException>(() => (null as HttpRequestBase).Token());
        }
    }
}